
#include <Archaism/Window.h>
#include <map>
#include <Windows.h>

_BeginNamespace(Radix)

/*
============================================================
== WindowMacro
============================================================
*/
#if(_RADIX_STRINGTYPE == _RADIX_STRINGTYPE_WCS)
#define WNDCLASSEX WNDCLASSEXW
#define RegisterClassEx RegisterClassExW
#define CreateWindowEx CreateWindowExW
#define SetWindowText SetWindowTextW
#else
#define WNDCLASSEX WNDCLASSEXA
#define RegisterClassEx RegisterClassExA
#define CreateWindowEx CreateWindowExA
#define SetWindowText SetWindowTextA
#endif
/*
============================================================
== WindowManager
============================================================
*/
class WindowManager
{
public:
	typedef std::map<HWND, Window*> WindowMap;

public:
	static bool registerWindow(Window& window)
	{
		HWND hWnd = (HWND)window.windowHandle();
		if(msWindows.find(hWnd) != msWindows.end())
		{
			return false;
		}
		msWindows[hWnd] = &window;
		return true;
	}

	static void unregisterWindow(Window& window)
	{
		HWND hWnd = (HWND)window.windowHandle();
		WindowMap::iterator windowIter = msWindows.find(hWnd);
		if(windowIter != msWindows.end())
		{
			msWindows.erase(windowIter);
		}
	}

	static Window* selectWindow(HWND hWnd)
	{
		WindowMap::iterator windowIter = msWindows.find(hWnd);
		if(windowIter == msWindows.end())
		{
			return nullptr;
		}
		return windowIter->second;
	}

	static LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		Window* window = selectWindow(hWnd);
		if(window != nullptr)
		{
			switch(msg)
			{
			case WM_SIZE:
				{
					Window::ResizeSignalMessage message;
					message.window = window;
					message.width = LOWORD(lParam);
					message.height = HIWORD(lParam);
					window->onResize(message);

					if(wParam == SIZE_MAXIMIZED)
					{
						Window::ActionSignalMessage message;
						message.window = window;
						window->onMaximize(message);
					}
					else if(wParam == SIZE_MINIMIZED)
					{
						Window::ActionSignalMessage message;
						message.window = window;
						window->onMinimize(message);
					}
				}
				return 0;

			case WM_MOVE:
				{
					Window::MoveSignalMessage message;
					message.window = window;
					message.x = LOWORD(lParam);
					message.y = HIWORD(lParam);
					window->onMove(message);
				}
				return 0;

			case WM_CLOSE:
				{
					Window::CloseSignalMessage message;
					message.window = window;
					message.cancel = false;
					window->onClose(message);
					if(! message.cancel)
					{
						DestroyWindow(hWnd);
					}
				}
				return 0;

			case WM_DESTROY:
				{
					Window::ActionSignalMessage message;
					message.window = window;
					window->onDestroy(message);
				}
				return 0;

			default:
				return DefWindowProc(hWnd, msg, wParam, lParam);
			}
		}

		return DefWindowProc(hWnd, msg, wParam, lParam);
	}

private:
	static WindowMap msWindows;
};

WindowManager::WindowMap WindowManager::msWindows = WindowManager::WindowMap();

/*
============================================================
== WindowAttribute
============================================================
*/
struct WindowAttribute
{
	HWND mHandle;
	String mText;
	bool mVisible;
};
/*
============================================================
== Window
============================================================
*/
Window::Window(const String& text, int width, int height, bool isFullScreen)
	:mAttribute(new WindowAttribute)
{
	mAttribute->mHandle = 0;
	mAttribute->mText = text;
	mAttribute->mVisible = true;
	
	HINSTANCE hInstance = GetModuleHandle(0);
	WNDCLASSEX	wc = {
		sizeof(WNDCLASSEX), CS_CLASSDC, WindowManager::windowProc,
		0, 0, hInstance,
		0, LoadCursor(0, IDC_ARROW),
		(HBRUSH)GetStockObject(WHITE_BRUSH),
		0, text.c_str(), 0};
	RegisterClassEx(&wc);

	DWORD dwStyle = (isFullScreen) ? (WS_POPUP | WS_SYSMENU) : (WS_OVERLAPPEDWINDOW); 
	mAttribute->mHandle = CreateWindowEx(
		0,  wc.lpszClassName,  
		text.c_str(),
		dwStyle,
		0, 0,
		width, height,
		0, 0, hInstance, 0);
	
	if(mAttribute->mHandle != 0)
	{
		WindowManager::registerWindow(*this);
		ActionSignalMessage message;
		message.window = this;
		this->onCreate(message);
	}
}

Window::~Window()
{
	WindowManager::unregisterWindow(*this);
	delete mAttribute;
}

uint Window::windowHandle() const
{
	return (uint)(mAttribute->mHandle);
}

const String& Window::text() const
{
	return mAttribute->mText;
}

void Window::setText(const String& text)
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		SetWindowText(hWnd, text.c_str());
		mAttribute->mText = text;
	}
}

bool Window::visible() const
{
	return mAttribute->mVisible;
}

void Window::setVisible(bool visible)
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		int cmdShow = visible ? SW_SHOW : SW_HIDE;
		ShowWindow(hWnd, cmdShow);
		UpdateWindow(hWnd);
		mAttribute->mVisible = visible;
	}
}

bool Window::fullScreen() const
{
	DWORD style = GetWindowLong(mAttribute->mHandle, GWL_STYLE);
	return (style & WS_POPUP) != 0;
}

void Window::setFullScreen(bool isFullScreen)
{
	DWORD style = (isFullScreen) ? (WS_POPUP | WS_SYSMENU) : (WS_OVERLAPPEDWINDOW); 
	SetWindowLong(mAttribute->mHandle, GWL_STYLE, style);
}

int Window::x() const
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		RECT rect;
		GetWindowRect(hWnd, &rect);
		return static_cast<int>(rect.left);
	}
	return 0;
}

int Window::y() const
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		RECT rect;
		GetWindowRect(hWnd, &rect);
		return static_cast<int>(rect.top);
	}
	return 0;
}

void Window::setLocation(int x, int y)
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		RECT rect;
		GetWindowRect(hWnd, &rect);
		int w = static_cast<int>(rect.right - rect.left);
		int h = static_cast<int>(rect.bottom - rect.top);
		MoveWindow(hWnd, x, y, w, h, TRUE);
	}
}

int Window::width() const
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		RECT rect;
		GetWindowRect(hWnd, &rect);
		return static_cast<int>(rect.right - rect.left);
	}
	return 0;
}

int Window::height() const
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		RECT rect;
		GetWindowRect(hWnd, &rect);
		return static_cast<int>(rect.bottom - rect.top);
	}
	return 0;
}

void Window::setSize(int w, int h)
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		RECT rect;
		GetWindowRect(hWnd, &rect);
		int x = static_cast<int>(rect.left);
		int y = static_cast<int>(rect.top);
		MoveWindow(hWnd, x, y, w, h, TRUE);
	}
}

void Window::center()
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		int screenWidth = GetSystemMetrics(SM_CXSCREEN);
		int screenHeight = GetSystemMetrics(SM_CYSCREEN);
		RECT rect;
		GetWindowRect(hWnd, &rect);
		int w = static_cast<int>(rect.right - rect.left);
		int h = static_cast<int>(rect.bottom - rect.top);
		int x = (screenWidth - w) / 2;
		int y = (screenHeight - h) / 2;
		MoveWindow(hWnd, x, y, w, h, TRUE);
	}
}

void Window::show()
{
	this->setVisible(true);
}

void Window::hide()
{
	this->setVisible(false);
}

void Window::close()
{
	HWND hWnd = mAttribute->mHandle;
	if(hWnd != 0)
	{
		PostMessage(hWnd, WM_CLOSE, 0, 0);
	}
}

_EndNamespace(Radix)
